﻿using System;
using System.Linq;
using System.Linq.Expressions;
using AntsCode.StorageClientExtensions.Helpers;
using Microsoft.WindowsAzure.StorageClient;

namespace AntsCode.StorageClientExtensions
{
    public class XTableServiceQueryProvider : IQueryProvider
    {
        public XTableServiceQueryProvider(TableServiceContext context, EventHandler<XTableServiceSendingRequestEventArgs> sendingRequest, RetryPolicy retryPolicy)
        {
            this.Context = context;
            this.SendingRequest = sendingRequest;
            this.RetryPolicy = retryPolicy;
        }

        public IQueryable CreateQuery(Expression expression)
        {
            Type elementType = TypeSystem.GetElementType(expression.Type);

            try
            {
                return (IQueryable)Activator.CreateInstance(typeof(XTableServiceQuery<>).MakeGenericType(elementType), new object[] { this, expression });
            }
            catch (System.Reflection.TargetInvocationException tie)
            {
                throw tie.InnerException;
            }
        }

        public IQueryable<TResult> CreateQuery<TResult>(Expression expression)
        {
            return new XTableServiceQuery<TResult>.XTableServiceOrderedQuery(expression, this);
        }

        public T Execute<T>(Expression expression)
        {
            bool IsEnumerable = (typeof(T).Name == "IEnumerable`1");

            return (T)this.Execute(expression, IsEnumerable);
        }

        public object Execute(Expression expression)
        {
            return this.Execute(expression, false);
        }

        private object Execute(Expression expression, bool IsEnumerable)
        {
            // The expression must represent a query over the data source.
            if (!IsQueryOverDataSource(expression))
                throw new InvalidProgramException("No query over the data source was specified.");

            MethodCallExpression me = (MethodCallExpression)expression;

            XTableServiceVisitor visitor = new XTableServiceVisitor(this.Context, this.SendingRequest, this.RetryPolicy, expression, this.ContinuationToken);

            // Update the continuation token
            this.ContinuationToken = visitor.ContinuationToken;

            return visitor.Data;
        }

        private bool IsQueryOverDataSource(Expression expression)
        {
            // If expression represents an unqueried IQueryable data source instance,
            // expression is of type ConstantExpression, not MethodCallExpression.
            return (expression is MethodCallExpression);
        }

        public TableServiceContext Context
        {
            get;
            private set;
        }

        public ContinuationToken ContinuationToken
        {
            get;
            set;
        }

        public RetryPolicy RetryPolicy
        {
            get;
            set;
        }

        public EventHandler<XTableServiceSendingRequestEventArgs> SendingRequest;
    }
}
